<?php

namespace addons\yunfood\library;

use addons\yunfood\model\Config;
use addons\yunfood\model\Norepeat;
use addons\yunfood\model\Order;
use addons\yunfood\model\PrinterDevice;
use addons\yunfood\model\PrinterRecord;
use addons\yunfood\model\Shop;
use addons\yunfood\model\SmsRecord;
use addons\yunfood\model\UserAuth;
use fast\Http;
use fast\Random;
use think\Cache;
use think\Exception;
use think\Paginator;

class Logic
{
    // 资源类别
    const SOURCE_GIFT        = 'gift';
    const SOURCE_GIFT_RECORD = 'gift_order';

    public static function getSourceList()
    {
        return [
            self::SOURCE_GIFT        => '礼品',
            self::SOURCE_GIFT_RECORD => '礼品订单',
        ];
    }

    /**
     * @param $openid
     * @param $source
     * @return array|bool|float|int|mixed|object|\stdClass|null
     * @throws \think\exception\DbException
     */
    public static function getUserAuthKey($openid, $source)
    {
        if (! $userAuth = UserAuth::get(['openid' => $openid, 'source' => $source])) {
            $userAuth = new UserAuth();
            $userAuth->source = $source;
            $userAuth->openid = $openid;
            $userAuth->auth_key = md5(uniqid(rand(), true));
            $userAuth->auth_key_expire = time() + 86400 * 7;
            $userAuth->user_id = 0;
            $userAuth->save();
        } elseif ($userAuth->auth_key_expire < time() + 300) {
            $userAuth->auth_key = md5(uniqid(rand(), true));
            $userAuth->auth_key_expire = time() + 86400 * 7;
            $userAuth->save();
        }

        return $userAuth->auth_key;
    }

    /**
     * @param $authKey
     * @param $source
     * @return array|bool|float|int|mixed|object|\stdClass|null
     * @throws \think\exception\DbException
     */
    public static function getUserAuthOpenid($authKey, $source)
    {
        $userAuth = UserAuth::get(['auth_key' => $authKey, 'source' => $source]);
        if ($userAuth && $userAuth->auth_key_expire > time()) {
            return $userAuth->openid;
        }

        return null;
    }

    /**
     * @param $openid
     * @param $source
     * @return array|bool|float|int|mixed|object|\stdClass|null
     * @throws \think\exception\DbException
     */
    public static function getUserAuthUid($openid, $source)
    {
        if (! $userAuth = UserAuth::get(['openid' => $openid, 'source' => $source])) {
            $userAuth = new UserAuth();
            $userAuth->source = $source;
            $userAuth->openid = $openid;
            $userAuth->auth_key = md5(uniqid(rand(), true));
            $userAuth->auth_key_expire = time() + 86400 * 7;
            $userAuth->user_id = 0;
            $userAuth->save();
        }

        return $userAuth->user_id;
    }

    /**
     * @param $userId
     * @param $openid
     * @param $source
     * @throws \think\exception\DbException
     */
    public static function bindUserAuth($userId, $openid, $source)
    {
        if ($openid && $source) {
            if (! $userAuth = UserAuth::get(['openid' => $openid, 'source' => $source])) {
                $userAuth = new UserAuth();
                $userAuth->source = $source;
                $userAuth->openid = $openid;
                $userAuth->auth_key = md5(uniqid(rand(), true));
                $userAuth->auth_key_expire = time() + 86400 * 7;
            }
            $userAuth->user_id = $userId;
            $userAuth->save();
        }
    }

    /**
     * @param $userId
     * @param $source
     */
    public static function unbindUserAuth($userId, $source)
    {
        UserAuth::update(['user_id' => 0], ['user_id' => $userId, 'source' => $source]);
    }

    /**
     * 获取小程序access_token
     *
     * @return array|false
     */
    public static function getWechatAccessToken($throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_BASE, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_BASE, 'app_secret');
        if (! $appId || ! $appSecret) {
            if ($throw) {
                throw new Exception('小程序AppId或AppSecret未配置');
            }

            return false;
        }

        return Cache::remember('accessToken:' . $appId, function () use ($appId, $appSecret, $throw) {
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $appSecret;
            $result = json_decode(Http::get($url), true);
            if (empty($result['access_token'])) {
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return $result['access_token'];
        }, 7200);
    }

    /**
     * 获取小程序登录code对应的openid
     *
     * @param $code
     * @return array|false
     */
    public static function getMiniCodeData($code, $throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_BASE, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_BASE, 'app_secret');
        if (! $appId || ! $appSecret) {
            if ($throw) {
                throw new Exception('小程序的AppId或AppSecret未配置');
            }

            return false;
        }
        // 获取openid
        $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appId . '&secret=' . $appSecret . '&js_code=' . $code . '&grant_type=authorization_code';
        $result = json_decode(Http::get($url), true);
        if (empty($result['openid'])) {
            if ($throw) {
                throw new Exception($result['errmsg']);
            }

            return false;
        }

        return $result;
    }

    /**
     * 获取小程序手机号code对应的信息
     *
     * @param $code
     * @return array|false
     */
    public static function getWechatPhoneData($code, $throw = true)
    {
        $retry = 1;
        while (true) {
            $accessToken = self::getWechatAccessToken($throw);

            // 获取手机信息
            $url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=' . $accessToken;
            $result = json_decode(Http::post($url, json_encode(['code' => $code])), true);
            // https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
            if (empty($result['phone_info'])) {
                if (in_array($result['errcode'], [40001, 40014, 41001, 42001])) {
                    Cache::rm('accessToken:' . Config::getGroupAttribute(Config::GROUP_BASE, 'app_id'));
                    if ($retry-- <= 0) {
                        if ($throw) {
                            throw new Exception($result['errmsg']);
                        }

                        return false;
                    }
                    continue;
                }
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return $result['phone_info'];
        }
    }

    /**
     * 获取小程序手机号code对应的信息
     *
     * @param $code
     * @return array|false
     */
    public static function getWechatQrcode($page, $scene, $options = [], $throw = true)
    {
        $retry = 1;
        while (true) {
            $accessToken = self::getWechatAccessToken($throw);

            // 获取手机信息
            $params = $options;
            $params['page'] = $page;
            $params['scene'] = $scene;
            $url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=' . $accessToken;
            $response = Http::post($url, json_encode($params));
            $result = json_decode($response, true);
            // https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html
            if ($result) {
                if (in_array($result['errcode'], [40001, 40014, 41001, 42001])) {
                    Cache::rm('accessToken:' . Config::getGroupAttribute(Config::GROUP_BASE, 'app_id'));
                    if ($retry-- <= 0) {
                        if ($throw) {
                            throw new Exception($result['errmsg']);
                        }

                        return false;
                    }
                    continue;
                }
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return $response;
        }
    }

    /**
     * 获取小程序手机号code对应的信息
     *
     * @param $code
     * @return array|false
     */
    public static function sendSubscribeMessage($openid, $page, $scene, $options = [], $throw = true)
    {
        $retry = 1;
        while (true) {
            $accessToken = self::getWechatAccessToken($throw);

            // 获取手机信息
            $params = $options;
            $params['template_id'] = $page;
            $params['page'] = '';
            $params['touser'] = $openid;
            $params['data'] = json_encode($params);
//            $params['miniprogram_state'] = 'formal';
//            $params['lang'] = 'zh_CN';
            $url = 'https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=' . $accessToken;
            $result = json_decode(Http::post($url, json_encode($params)), true);
            // https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html
            if (isset($result['errcode'])) {
                if (in_array($result['errcode'], [40001, 40014, 41001, 42001])) {
                    Cache::rm('accessToken:' . Config::getGroupAttribute(Config::GROUP_BASE, 'app_id'));
                    if ($retry-- <= 0) {
                        if ($throw) {
                            throw new Exception($result['errmsg']);
                        }

                        return false;
                    }
                    continue;
                }
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return true;
        }
    }

    /**
     * 解密小程序的加密信息
     *
     * @param $userId
     * @param $encryptedData
     * @param $iv
     * @return false|mixed
     */
    public static function decryptWechatData($sessionKey, $encryptedData, $iv)
    {
        if (! $appid = Config::getGroupAttribute(Config::GROUP_BASE, 'app_id')) {
            return false;
        }
        $wxBiz = new WXBizDataCrypt($appid, $sessionKey);
        $errCode = $wxBiz->decryptData($encryptedData, $iv, $data);
        if ($errCode != 0) {
            return false;
        }

        return json_decode($data, true);
    }

    /**
     * 获取公众号access_token
     *
     * @return array|false
     */
    public static function getMpAccessToken($throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_MP, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_MP, 'app_secret');
        if (! $appId || ! $appSecret) {
            if ($throw) {
                throw new Exception('公众号AppId或AppSecret未配置');
            }

            return false;
        }

        // 获取access_token
        return Cache::remember('mpAccessToken:' . $appId, function () use ($appId, $appSecret, $throw) {
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $appSecret;
            $result = json_decode(Http::get($url), true);
            if (empty($result['access_token'])) {
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return $result['access_token'];
        }, 7200);
    }

    /**
     * 获取微信公众号code对应的openid
     *
     * @param $code
     * @return array|false
     */
    public static function getMpCodeData($code, $throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_MP, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_MP, 'app_secret');
        if (! $appId || ! $appSecret) {
            if ($throw) {
                throw new Exception('公众号AppId或AppSecret未配置');
            }

            return false;
        }

        // 获取openid
        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appId . '&secret=' . $appSecret . '&code=' . $code . '&grant_type=authorization_code';
        $result = json_decode(Http::get($url), true);
        if (empty($result['openid'])) {
            if ($throw) {
                throw new Exception($result['errmsg']);
            }

            return false;
        }

        return $result;
    }

    /**
     * 获取微信公众号JS ticket信息
     *
     * @return array|false
     */
    public static function getMpJsTicket($throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_MP, 'app_id');

        return Cache::remember('mpJsTicket:' . $appId, function () use ($appId, $throw) {
            $retry = 1;
            while (true) {
                $accessToken = self::getMpAccessToken($throw);

                // 获取JS ticket信息
                $url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' . $accessToken . '&type=jsapi';
                $result = json_decode(Http::get($url), true);
                if (empty($result['ticket'])) {
                    if (in_array($result['errcode'], [40001, 40014, 41001, 42001])) {
                        Cache::rm('mpAccessToken:' . $appId);
                        if ($retry-- <= 0) {
                            if ($throw) {
                                throw new Exception($result['errmsg']);
                            }

                            return false;
                        }
                        continue;
                    }
                    if ($throw) {
                        throw new Exception($result['errmsg']);
                    }

                    return false;
                }

                return $result['ticket'];
            }
        }, 7200);
    }

    /**
     * @param string $url
     * @param bool $throw
     * @return array|false
     */
    public static function getMpConfigData($url = '', $throw = true)
    {
        $jsTicket = self::getMpJsTicket($throw);
        if (! $throw && ! $jsTicket) {
            return false;
        }
        $noncestr = Random::alnum(8);
        $timestamp = time();
        $signString = "jsapi_ticket={$jsTicket}&noncestr={$noncestr}&timestamp={$timestamp}&url={$url}";
        $signature = sha1($signString);

        $appId = Config::getGroupAttribute(Config::GROUP_MP, 'app_id');

        return [
            'appId'     => $appId,
            'nonceStr'  => $noncestr,
            'timestamp' => $timestamp,
            'signature' => $signature,
        ];
    }


    /**
     * 获取分页数据
     *
     * @param Paginator $paginator
     * @param array $fields
     * @return array
     */
    public static function getPageData(Paginator $paginator, $fields = [])
    {
        $result = $paginator->toArray();
        $items = $fields ? Super::arrayGetSet($paginator->items(), $fields, true) : $result['data'];

        return [
            'total'        => $result['total'],
            'per_page'     => $result['per_page'],
            'current_page' => $result['current_page'],
            'last_page'    => $result['last_page'],
            'items'        => $items
        ];
    }

    /**
     * 给模型设置初始值
     */
    public static function setDefaultValue(&$model, $attributes)
    {
        $data = $model->getData(null);
        foreach ($attributes as $field => $value) {
            if (! array_key_exists($field, $data)) {
                $model->$field = $value;
            }
        }
    }

    /**
     * 给手机号发验证码
     *
     * @param $code
     * @return array|false
     */
    public static function sendPhoneCaptcha($phone, $captcha)
    {
        $templateCode = Config::getGroupAttribute(Config::GROUP_SMS, 'template_code');
        $params = ['code' => $captcha];

        return self::sendSms($phone, $templateCode, $params);
    }

    /**
     * 获取短信参数
     *
     * @param $template
     * @param $data
     * @return array
     */
    public static function getSmsParams($template, $data)
    {
        preg_match_all("/\\$\{([a-zA-Z0-9_]+)\}/", $template, $matches);
        if (empty($matches[1])) {
            return [];
        }
        $params = [];
        foreach ($data as $key => $value) {
            if (in_array($key, $matches[1])) {
                $params[$key] = $value;
            }
        }

        return $params;
    }

    /**
     * 发送短信
     *
     * @param $code
     * @return array|false
     */
    public static function sendSms($phone, $templateCode, $params)
    {
        $smsConfig = Config::getGroupAttributes(Config::GROUP_SMS);
        $accessKeyId = $smsConfig['app_key_id'] ?? '';
        $accessKeySecret = $smsConfig['app_key_secret'] ?? '';
        $signName = $smsConfig['sign_name'] ?? '';
        $error = '';
        try {
            $response = Super::sendAliyunPhoneCode($phone, $params, $templateCode, $accessKeyId, $accessKeySecret,
                $signName);
            if (! $response) {
                $error = '网络错误';
            } elseif ($response['Code'] !== 'OK') {
                $error = $response['Message'];
            }
        } catch (Exception $e) {
            $response = $e->getMessage();
            $error = $e->getMessage();
        }

        // 发送记录
        $smsRecord = new SmsRecord();
        $smsRecord->phone = $phone;
        $smsRecord->template_code = $templateCode;
        $smsRecord->params = json_encode($params);
        $smsRecord->response = json_encode($response);
        $smsRecord->save();

        return $error;
    }

    /**
     * 查询快递物流公司
     *
     * @param $appCode
     * @param $number
     * @param $company
     * @return false|mixed|string
     */
    public static function queryExpressCompany($cacheDuration = 86400)
    {
        if (! $appcode = Config::getGroupAttribute(Config::GROUP_EXPRESS, 'aliyun_appcode')) {
            return [];
        }

        return Cache::remember('globalExpressLists', function () use ($appcode) {
            $api = 'https://wdexpress.market.alicloudapi.com/globalExpressLists';
            $response = Super::requestAliCloudApi($appcode, $api, 'GET', ['type' => 'ALL']);
            $response = json_decode($response, true);

            return $response['result'];
        }, $cacheDuration);
    }

    /**
     * 查询快递物流信息
     * 注：顺丰必须传手机号
     *
     * @param $appCode
     * @param $number
     * @param $company
     * @return false|mixed|string
     */
    public static function queryExpressTrack($company, $number, $phone = '', $cacheDuration = 600)
    {
        $cacheKey = 'expressTrack:' . $company . ':' . $number . ':' . $phone;
        if (! $appcode = Config::getGroupAttribute(Config::GROUP_EXPRESS, 'aliyun_appcode')) {
            throw new Exception('阿里云物流插件appcode未配置');
        }
        if ($company === 'SF') {
            if (empty($phone)) {
                throw new Exception('顺丰必须填写手机号，才能查询物流信息');
            }
            $number .= ':' . substr($phone, -4, 4);
        }

        return Cache::remember($cacheKey, function () use ($appcode, $number, $company) {
            $api = 'https://wdexpress.market.alicloudapi.com/gxali';
            $trackInfo = Super::requestAliCloudApi($appcode, $api, 'GET', ['n' => $number, 't' => $company]);
            $trackResult = json_decode($trackInfo, true);
            $result = ['number' => $trackResult['LogisticCode']];

            // 状态
            $state = '未知状态';
            switch ($trackResult['State']) {
                case -1:
                    $state = '单号或快递公司代码错误';
                    break;
                case 0:
                    $state = '暂无轨迹';
                    break;
                case 1:
                    $state = '快递收件';
                    break;
                case 2:
                    $state = '在途中';
                    break;
                case 3:
                    $state = '已签收';
                    break;
                case 4:
                    $state = '问题件';
                    break;
                case 5:
                    $state = '疑难件（收件人拒绝签收，地址有误或不能送达派送区域，收费等原因无法正常派送）';
                    break;
                case 6:
                    $state = '退件签收';
                    break;
            }
            $result['state'] = $trackResult['State'];
            $result['state_text'] = $state;
            $result['company'] = $trackResult['ShipperCode'] ?? '未知';

            // 物流
            $trackList = [];
            foreach ($trackResult['Traces'] as $item) {
                $trackList[] = [
                    'time'    => $item['AcceptTime'],
                    'context' => $item['AcceptStation'],
                ];
            }
            $result['track_list'] = array_reverse($trackList);

            return $result;
        }, $cacheDuration);
    }

    /**
     * @param $assertion
     * @param $description
     */
    public static function niceAssert($assertion, $description = '操作失败')
    {
        if (is_string($description)) {
            assert_options(ASSERT_EXCEPTION, 1);
            $description = new Exception($description);
        }
        assert($assertion, $description);
    }

    // 生成业务单号
    public static function generateBusinessNo($scene = 'business_no')
    {
        $count = 1;
        while (true) {
            $time = time();
            $uniq = str_pad($time % 86400, 5, '0', STR_PAD_LEFT);
            $businessNo = date('Ymd', $time) . $uniq . random_int(0, 9);
            if ($scene === 'nickname') {
                $businessNo = substr($businessNo, 2);
            }
            if (Norepeat::tryAddData($scene, $businessNo, true)) {
                break;
            }
            if ($count == 0) {
                throw new Exception('系统错误，请稍后再试');
            }
            $count--;
        }

        return $businessNo;
    }

    public static function getGoodsNames($goodsData)
    {
        $goodsNames = [];
        if (! empty($goodsData['spec_data'])) {
            $goodsNames[] = $goodsData['spec_data']['spec_text'];
        }
        if (! empty($goodsData['attr_list'])) {
            foreach ($goodsData['attr_list'] as $sItem) {
                $goodsNames[] = $sItem['name'] . '(' . $sItem['item'] . ')';
            }
        }
        if (! empty($goodsData['note_list'])) {
            foreach ($goodsData['note_list'] as $sItem) {
                $goodsNames[] = $sItem;
            }
        }
        if (! empty($goodsData['addon_list'])) {
            foreach ($goodsData['addon_list'] as $sItem) {
                $goodsNames[] = '加' . $sItem['name'];
            }
        }

        return implode(', ', $goodsNames);
    }

    /**
     * 开始打印
     *
     * @param $order
     * @param $scene
     * @throws Exception
     */
    public static function startPrint($order, $scene)
    {
        // 打印内容
        $printData = Super::arrayGetSet($order, [
            'orderNo'        => 'order_no',  // 订单编号
            'enjoyType'      => function ($item) {  // 就餐方式
                $enjoyType = $item->enjoyTypeText;
                if ($item->enjoy_type == Order::ENJOY_TYPE_MEAT) {
                    $enjoyType = '    桌号：' . ($item->deskData->number ?? '--');
                } elseif ($item->enjoy_type == Order::ENJOY_TYPE_MEAT) {
                    $enjoyType = '    电话：' . ($item->contact ?: '--');
                }

                return $enjoyType;
            },
            'orderRemark'    => 'note',  // 订单备注
            'totalAmount'    => 'payment',  // 菜品总价
            'discountAmount' => 'discount',  // 优惠金额
            'balanceAmount'  => 'balance',  // 余额支付
            'cashAmount'     => 'payment_rel',  // 现金金额
            'paymentTime'    => function ($item) {  // 支付时间
                return date('Y-m-d H:i', $item->payment_time);
            },
            'orderTime'      => function ($item) {  // 下单时间
                return date('Y-m-d H:i', $item->created_at);
            },
            'shopName'       => function ($item) {  // 门店名称
                return $item->shopData->name ?? '--';
            },
        ]);
        $deviceList = PrinterDevice::where('find_in_set(:sid, `shop_ids`) or shop_ids=\'\'', ['sid' => $order->shop_id])
            ->where('find_in_set(:scene, `scenes`)', ['scene' => $scene])
            ->show()->select();
        foreach ($deviceList as $item) {
            $printConf = Config::getGroupAttributes($item->brand);
            if (! $orderTemplate = $printConf['order_template'] ?? '') {
                continue;
            }
            // 菜品详情
            $foodsDetail = '';
            // 名称14 单价6 数量3 金额6，这里的字节数可按自己需求自由改写，14+6+3+6再加上代码写的3个空格就是32了，58mm打印机一行总占32字节
            if ($item->brand == PrinterDevice::BRAND_FEIE) {
                $foodsDetail = self::getFoodsDetail($order->orderItemlistData, 14, 6, 3, 6, '<BR>');
            } elseif ($item->brand == PrinterDevice::BRAND_YILIANYUN) {
                $foodsDetail = self::getFoodsDetail($order->orderItemlistData, 14, 6, 3, 6, '\n');
            } elseif ($item->brand == PrinterDevice::BRAND_ZHONGWU) {
                $foodsDetail = self::getFoodsDetail($order->orderItemlistData, 14, 6, 3, 6, '<RN>');
            }
            $printData['foodsDetail'] = $foodsDetail;

            // 打印设备
            $printData['deviceName'] = $item['name'];
            $content = preg_replace_callback("/\\\${([a-zA-Z0-9_]+)\}/", function ($matches) use ($printData) {
                return $printData[$matches[1]] ?? '';
            }, $orderTemplate);

            // 进行打印
            list($params, $response) = Printer::printContent($item->brand, $content, $item, false);

            // 打印记录
            $printerRecord = new PrinterRecord();
            $printerRecord->device_id = $item->id;
            $printerRecord->order_no = $order->order_no;
            $printerRecord->brand = $item->brand;
            $printerRecord->content = $content;
            $printerRecord->params = is_string($params) ? $params : json_encode($params);
            $printerRecord->response = is_string($response) ? $response : json_encode($response);
            $printerRecord->save();
        }
    }

    /**
     * 获取菜品详情
     *
     * @param $foodList
     * @param $A
     * @param $B
     * @param $C
     * @param $D
     * @return string
     */
    public static function getFoodsDetail($foodList, $A, $B, $C, $D, $br)
    {
        // 58mm打印机一行总占32字节
        $orderInfo = '名称           单价  数量 金额' . $br;
        $orderInfo .= '--------------------------------' . $br;
        foreach ($foodList as $food) {
            $name = $food->goods_name;
            $price = $food->goods_price;
            $count = $food->goods_count;
            $totalPrice = $food->payment;
            $kw1 = '';
            $kw2 = '';
            $kw3 = '';
            $kw4 = '';
            $str = $name;
            $blankNum = $A;  // 名称控制为14个字节
            $lan = mb_strlen($str, 'utf-8');
            $m = 0;
            $j = 1;
            $blankNum++;
            $result = array();
            if (strlen($price) < $B) {
                $k1 = $B - strlen($price);
                for ($q = 0; $q < $k1; $q++) {
                    $kw1 .= ' ';
                }
                $price = $price . $kw1;
            }
            if (strlen($count) < $C) {
                $k2 = $C - strlen($count);
                for ($q = 0; $q < $k2; $q++) {
                    $kw2 .= ' ';
                }
                $count = $count . $kw2;
            }
            if (strlen($totalPrice) < $D) {
                $k3 = $D - strlen($totalPrice);
                for ($q = 0; $q < $k3; $q++) {
                    $kw4 .= ' ';
                }
                $totalPrice = $totalPrice . $kw4;
            }
            for ($i = 0; $i < $lan; $i++) {
                $new = mb_substr($str, $m, $j, 'utf-8');
                $j++;
                if (mb_strwidth($new, 'utf-8') < $blankNum) {
                    if ($m + $j > $lan) {
                        $m = $m + $j;
                        $tail = $new;
                        $tStr = iconv('UTF-8', 'GBK//IGNORE', $new);
                        $k = $A - strlen($tStr);
                        for ($q = 0; $q < $k; $q++) {
                            $kw3 .= ' ';
                        }
                        if ($m == $j) {
                            $tail .= $kw3 . ' ' . $price . ' ' . $count . ' ' . $totalPrice;
                        } else {
                            $tail .= $kw3 . $br;
                        }
                        break;
                    }
                    $next_new = mb_substr($str, $m, $j, 'utf-8');
                    if (mb_strwidth($next_new, 'utf-8') < $blankNum) {
                        continue;
                    }
                    $m = $i + 1;
                    $result[] = $new;
                    $j = 1;
                }
            }
            $head = '';
            foreach ($result as $key => $value) {
                if ($key < 1) {
                    $tStr = iconv('UTF-8', 'GBK//IGNORE', $value);
                    $tLen = strlen($tStr);
                    if ($tLen == 13) {
                        $value = $value . ' ';
                    }
                    $head .= $value . ' ' . $price . ' ' . $count . ' ' . $totalPrice;
                } else {
                    $head .= $value . $br;
                }
            }
            $orderInfo .= $head . $tail;
            if ($goodsNames = $food->goodsNames) {
                $orderInfo .= $goodsNames . $br;
            }
        }

        return $orderInfo;
    }

    public static function hourTimeToInt($hourTime)
    {
        return date('Gi', strtotime($hourTime));
    }

    public static function intToHourTime($int)
    {
        $int .= '';
        if (strlen($int) == 3) {
            $int = '0' . $int;
        }

        return substr($int, 0, 2) . ':' . substr($int, 2, 2);
    }


    /**
     * 生成海报图片
     *
     * @param array 参数：包括图片和文字
     * @param string $filename 生成文件名：不传此参数则不生成文件，直接输出图片
     * @return false|mixed|string
     */
    public static function createPoster($data = [], $filename = '')
    {
        if (empty($filename)) {
            header("content-type: image/png");
        }
        $imageDefault = [
            'left'    => 0,
            'top'     => 0,
            'right'   => 0,
            'bottom'  => 0,
            'width'   => 100,
            'height'  => 100,
            'opacity' => 100
        ];
        $textDefault = [
            'text'      => '',
            'left'      => 0,
            'top'       => 0,
            'fontSize'  => 32,  // 字号
            'fontColor' => '255,255,255',  // 字体颜色
            'angle'     => 0,
        ];

        // 背景图
        $background = $data['background'];
        $backgroundInfo = getimagesize($background);
        $backgroundFun = 'imagecreatefrom' . image_type_to_extension($backgroundInfo[2], false);
        $background = $backgroundFun($background);
        $backgroundWidth = imagesx($background);  // 背景宽度
        $backgroundHeight = imagesy($background);  // 背景高度
        $imageRes = imageCreatetruecolor($backgroundWidth, $backgroundHeight);
        $color = imagecolorallocate($imageRes, 0, 0, 0);
        imagefill($imageRes, 0, 0, $color);
        // imageColorTransparent($imageRes, $color);  // 颜色透明
        imagecopyresampled($imageRes, $background, 0, 0, 0, 0,
            imagesx($background), imagesy($background), imagesx($background), imagesy($background));

        // 处理贴图
        if (! empty($data['image'])) {
            foreach ($data['image'] as $item) {
                $item = array_merge($imageDefault, $item);
                $info = getimagesize($item['url']);
                $function = 'imagecreatefrom' . image_type_to_extension($info[2], false);
                // 如果传的是字符串图像流
                if ($item['stream']) {
                    $info = getimagesizefromstring($item['url']);
                    $function = 'imagecreatefromstring';
                }
                $res = $function($item['url']);
                $resWidth = $info[0];
                $resHeight = $info[1];
                // 建立画板 ，缩放图片至指定尺寸
                $canvas = imagecreatetruecolor($item['width'], $item['height']);
                imagefill($canvas, 0, 0, $color);
                // 关键函数，参数（目标资源，源，目标资源的开始坐标x、y，源资源的开始坐标x、y，目标资源的宽高w、h，源资源的宽高w、h）
                imagecopyresampled($canvas, $res, 0, 0, 0, 0, $item['width'], $item['height'], $resWidth, $resHeight);
                $item['left'] = $item['left'] < 0 ? $backgroundWidth - abs($item['left']) - $item['width'] : $item['left'];
                $item['top'] = $item['top'] < 0 ? $backgroundHeight - abs($item['top']) - $item['height'] : $item['top'];
                // 放置图像
                imagecopymerge($imageRes, $canvas, $item['left'], $item['top'], $item['right'], $item['bottom'],
                    $item['width'], $item['height'], $item['opacity']);  // 左，上，右，下，宽度，高度，透明度
            }
        }

        // 处理文字
        if (! empty($data['text'])) {
            foreach ($data['text'] as $item) {
                $item = array_merge($textDefault, $item);
                list($R, $G, $B) = explode(',', $item['fontColor']);
                $fontColor = imagecolorallocate($imageRes, $R, $G, $B);
                if (isset($item['align'])) {
                    if ($item['align'] === 'center') {
                        $bbox = imagettfbbox($item['fontSize'], 0, $item['fontPath'], $item['text']);
                        $textWidth = $bbox[2] - $bbox[0];
                        $item['left'] = ($backgroundWidth - $textWidth) / 2;
                    }
                } else {
                    $item['left'] = $item['left'] < 0 ? $backgroundWidth - abs($item['left']) : $item['left'];
                }
                $item['top'] = $item['top'] < 0 ? $backgroundHeight - abs($item['top']) : $item['top'];
                imagettftext($imageRes, $item['fontSize'], $item['angle'], $item['left'], $item['top'], $fontColor,
                    $item['fontPath'], $item['text']);
            }
        }

        // 生成图片
        if ($filename) {
            // 保存到本地
            $res = imagepng($imageRes, $filename, 9);
            imagedestroy($imageRes);
            if (! $res) {
                return false;
            }

            return $filename;
        }

        imagejpeg($imageRes);     //在浏览器上显示
        imagedestroy($imageRes);
    }

    /**
     * 获取百度access_token
     *
     * @return array|false
     */
    public static function getBaiduAccessToken($cacheDuration = 86400)
    {
        $noticeConfig = Config::getGroupAttributes(Config::GROUP_NOTICE);
        $appId = $noticeConfig['app_id'] ?? '';
        $appSecret = $noticeConfig['app_secret'] ?? '';
        if (! $appId || ! $appSecret) {
            return false;
        }

        // 获取access_token
        $retry = 1;
        while (true) {
            $accessToken = Cache::remember('baiduAccessToken:' . $appId, function () use ($appId, $appSecret) {
                // https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu
                $url = 'https://aip.baidubce.com/oauth/2.0/token?client_id=' . $appId . '&client_secret=' . $appSecret . '&grant_type=client_credentials';
                $result = json_decode(Http::get($url), true);
                if (empty($result['access_token'])) {
                    return false;
                }

                return $result['access_token'];
            }, 5);
            if (! $accessToken) {
                Cache::rm('baiduAccessToken:' . $appId);
                if ($retry-- <= 0) {
                    return false;
                }
                continue;
            }

            return $accessToken;
        }
    }

    /**
     * 获取小程序手机号code对应的信息
     *
     * @param $code
     * @return array|false
     */
    public static function baiduText2Audio($content, $options = [])
    {
        $retry = 1;
        while (true) {
            if (! $accessToken = self::getBaiduAccessToken()) {
                return false;
            }

            // 获取手机信息
            $params = [
                'tex'  => urlencode($content),
                'lan'  => 'zh',
                'tok'  => $accessToken,
                'ctp'  => 1,
                'cuid' => 'yunfood',
                'spd'  => 7,
                'pit'  => 5,
                'vol'  => 9,
                'per'  => 0,
                'aue'  => 3,
            ];
            $params = array_merge($params, $options);
            // https://ai.baidu.com/ai-doc/SPEECH/mlbxh7xie
            $response = Http::get('https://tsn.baidu.com/text2audio', $params);
            $result = json_decode($response, true);
            if ($result) {
                if (isset($result['err_no'])) {
                    if ($result['err_no'] == 502) {
                        Cache::rm('baiduAccessToken:' . Config::getGroupAttribute(Config::GROUP_NOTICE, 'app_id'));
                        if ($retry-- <= 0) {
                            return false;
                        }
                        continue;
                    }
                    throw new Exception($result['err_msg']);
                }
            }

            return $response;
        }
    }

    public static function getShopNames($shopIds)
    {
        if (empty($shopIds)) {
            return '不限';
        }
        $shopList = Shop::getOptionList([], true);
        $shopIds = is_array($shopIds) ? $shopIds : explode(',', $shopIds);
        $shopNames = [];
        foreach ($shopIds as $item) {
            if (isset($shopList[$item])) {
                $shopNames[] = $shopList[$item];
            }
        }

        return implode(',', $shopNames);
    }

    /**
     * @param $callback
     */
    public static function quickTryCatch($callback)
    {
        try {
            $result = $callback();
            return [0, $result];
        } catch (Exception $e) {
            return [$e->getCode(), $e->getMessage()];
        }
    }
}
